package com.jiao.datasource.proxy;

import com.jiao.comm.utils.LogUtil;
import com.jiao.datasource.listener.ListenerExce;
import com.jiao.datasource.parse.SQLCommand;
import com.jiao.datasource.track.RecordSQLDetail;
import com.jiao.datasource.track.SQLExecTrack;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @Author : Vincent.jiao
 * @Date : 2021/9/4 22:00
 * @Version : 1.0
 */
public class StatementProxy implements InvocationHandler {
    protected Connection conn;
    protected Statement ps;

    public StatementProxy (Statement ps){
        this.ps = ps;

        if(ps != null){
            try {
                conn = ps.getConnection();
            } catch (SQLException e) {
                LogUtil.error(e.getMessage(), e);
            }
        }
    }

    /**
     *
     * ResultSet executeQuery(String sql)
     * void addBatch( String sql )
     * void clearBatch()
     * int[] executeBatch()
     * int executeUpdate(String sql)
     * int executeUpdate(String sql, int autoGeneratedKeys)
     * int executeUpdate(String sql, int columnIndexes[])
     * int executeUpdate(String sql, String columnNames[])
     * int executeUpdate(String sql, int columnIndexes[])
     * int executeUpdate(String sql, String columnNames[])
     * boolean execute(String sql, int autoGeneratedKeys)
     * boolean execute(String sql, int columnIndexes[])
     * boolean execute(String sql, String columnNames[])
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return invokeBase(proxy, method, args);
    }

    protected Object invokeBase(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Object value = null;
        boolean isExecInvoke = true;    //是否执行 invoke
        boolean isExecListener = false; //是否执行监听器

        try {
            switch (methodName){
                case "addBatch" :
                    batch(proxy, method, args);
                    break;

                case "executeBatch" :
                    batch(proxy, method, args);
                    isExecListener = true;
                    break;

                case "executeUpdate" :
                    value = executeUpdate(proxy, method, args);
                    break;

                case "executeQuery" :
                    executeQuery(proxy,method, args);
                    break;

                case "execute" :
                    isExecInvoke = false;
                    value = execute(proxy, method, args);
                    break;
            }

            if(isExecInvoke){
                value = execInvoke(proxy, method, args, isExecListener);
            }

        } catch (Exception e){
            throw e;
        }

        return value;
    }

    protected void batch(Object proxy, Method method, Object[] args) {
        switch (method.getName()) {
            case "addBatch":
                if(args == null || args.length == 0) {
                    RecordSQLDetail.recordAddBatch();
                } else {
                    RecordSQLDetail.recordAddBatch(args[0].toString());
                }

                break;
            case "executeBatch":
                break;
        }
    }

    protected void executeQuery(Object proxy, Method method, Object[] args) throws SQLException {
        if (args != null && args.length > 0) {
            RecordSQLDetail.recordSql(ps, args[0].toString());
        }
    }

    protected Object execute(Object proxy, Method method, Object[] args) throws Throwable {

        if (args != null && args.length > 0) {
            RecordSQLDetail.recordSql(ps, args[0].toString());
        }

        Object value = execInvoke(proxy, method, args, true);
        setCount(method.getName(), value);
        return value;
    }

    protected Object executeUpdate(Object proxy, Method method, Object[] args) throws Throwable {
        Object value = execInvoke(proxy, method, args, true);
        if (args != null && args.length > 0) {
            RecordSQLDetail.recordSql(ps, args[0].toString());
        }
        setCount(method.getName(), value);

        return value;
    }

    public Object execInvoke(Object proxy, Method method, Object[] args) throws Throwable{
        return execInvoke(proxy, method, args, false);
    }

    public Object execInvoke(Object proxy, Method method, Object[] args, boolean isListener) throws Throwable{
        if (isListener){
            ListenerExce.execBefore(SQLExecTrack.getCurrExceSQL());
        }

        Object value = method.invoke(ps, args);
        setCount(method.getName(), value);

        if (isListener){
            ListenerExce.execAfter(SQLExecTrack.getCurrExceSQL());
        }
        return value;
    }

    protected void setCount(String methodName, Object value) throws SQLException {
        switch (methodName) {
            case "executeUpdate" :
                RecordSQLDetail.setCount(Integer.parseInt(value.toString()));
                break;
            case "executeBatch" :
                if (value instanceof int[]) {
                    int count = 0;
                    int[] arrCount= (int[]) value;
                    for (int i = 0, size = arrCount.length; i < size; i++) {
                        count += arrCount[i];
                    }

                    RecordSQLDetail.setCount(count);
                }
                break;
            case "execute" :
                Boolean isResult = Boolean.valueOf(value.toString());
                if (!isResult) {
                    RecordSQLDetail.setCount(ps.getUpdateCount());
                }

                break;
        }
    }

    public Connection getConn() {
        return conn;
    }
}